/*
Copyright (c) 2008  Franklin Schmidt <fschmidt@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

package fschmidt.util.servlet;

import fschmidt.util.java.Base64;
import fschmidt.util.java.HtmlUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpUtils;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;


public final class ServletUtils {

	private ServletUtils() { throw new RuntimeException(); }

	public static String getQueryString(HttpServletRequest request) {
		return getQueryString(request,0);
	}

	public static String getQueryString(HttpServletRequest request,int maxValueLen) {
		String method = request.getMethod();
		if( method.equals("GET") )
			return request.getQueryString();
		if( !method.equals("POST") && !method.equals("HEAD") )
			throw new RuntimeException(method);
		Enumeration en = request.getParameterNames();
		StringBuilder queryBuf = new StringBuilder();
		if( !en.hasMoreElements() )
			return null;
		do {
			String param = (String)en.nextElement();
			String value = request.getParameter(param);
			if( maxValueLen > 0 ) {
				int len = value.length();
				if( len > maxValueLen )
					value = value.substring(0,maxValueLen) + "..." + (len-maxValueLen);
			}
			queryBuf.append(param);
			queryBuf.append('=');
			queryBuf.append(value);
			queryBuf.append('&');
		} while( en.hasMoreElements() );
		queryBuf.deleteCharAt(queryBuf.length() - 1);
		return queryBuf.toString();
	}

	public static String getCurrentURL(HttpServletRequest request) {
		return getCurrentURL(request,0);
	}

	public static String getCurrentURL(HttpServletRequest request,int maxValueLen) {
//		StringBuffer buf = HttpUtils.getRequestURL(request);
		StringBuffer buf = request.getRequestURL();
		String qStr = getQueryString(request,maxValueLen);
		if(qStr != null && qStr.length() > 0) {
			buf.append('?');
			buf.append(qStr);
		}
		return buf.toString();
	}

	public static String fullURL(HttpServletRequest request,String relativeURL)
		throws MalformedURLException
	{
		return new URL(new URL(request.getRequestURL().toString()),relativeURL).toString();
	}

	public static String getHost(HttpServletRequest request) {
/*
		String host = request.getHeader("host");
		if( host.endsWith(":80") )
			host = host.substring(0,host.length()-3);
		return host;
*/
		String host = request.getServerName();
		int port = request.getServerPort();
		if( port != 80 ) {
			host += ":" + port;
		}
		return host;
	}

	static String getContextUrl(String scheme,String host,String contextPath) {
		StringBuilder buf = new StringBuilder();
		buf.append( scheme );
		buf.append( "://" );
		buf.append( host );
		buf.append( contextPath );
		return buf.toString();
	}

	public static String getContextURL(HttpServletRequest request) {
		return getContextUrl( request.getScheme(), getHost(request), request.getContextPath() );
	}

	public static String getServletPath(HttpServletRequest request,String relativeURL)
		throws MalformedURLException
	{
		int i = relativeURL.indexOf('?');
		if( i != -1 )
			relativeURL = relativeURL.substring(0,i);
		String url = fullURL(request,relativeURL);
		String context = getContextURL(request);
		if( !url.startsWith(context) )
			throw new RuntimeException("context="+context+" url="+url);
		return url.substring(context.length());
	}

	public static Map<String,String[]> getParameterMap(String url) {
		int i = url.indexOf('?');
		if( i == -1 )
			return Collections.emptyMap();
		String query = url.substring(i+1);
		return parseQueryString(query);
	}

	@SuppressWarnings("unchecked")
	private static Map<String,String[]> parseQueryString(String query) {
		return HttpUtils.parseQueryString(query);
	}

	public static String getQueryString(Map<String,String[]> params) {
		StringBuilder buf = new StringBuilder();
		for( Iterator<Map.Entry<String,String[]>> iter=params.entrySet().iterator(); iter.hasNext(); ) {
			Map.Entry<String,String[]> entry = iter.next();
			String name = entry.getKey();
			String[] values = entry.getValue();
			for( int i=0; i<values.length; i++ ) {
				if( buf.length() > 0 )
					buf.append( '&' );
				buf.append( name );
				buf.append( '=' );
				buf.append( HtmlUtils.urlEncode(values[i]) );
			}
		}
		return buf.toString();
	}

	private static String escape(String value) {
		return value.replaceAll(";", "%3B");
	}

	private static String unescape(String value) {
		return value.replaceAll("%3B", ";");
	}

	private static Cookie getCookie(HttpServletRequest request,String name) {
		Cookie[] cookies = request.getCookies();
		if( cookies == null )
			return null;
		for (Cookie cookie : cookies) {
			if (cookie.getName().equals(name))
				return cookie;
		}
		return null;
	}

	public static String getCookieValue(HttpServletRequest request,String name) {
		Cookie cookie = getCookie(request,name);
		return cookie==null ? null : unescape(cookie.getValue());
	}

	public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) {
		Cookie cookie = getCookie(request,name);
		if( cookie==null || !cookie.getValue().equals(value) ) {
			cookie = new Cookie(name, escape(value));
			cookie.setPath("/");
			if (domain != null && domain.length() > 0)
				cookie.setDomain(domain);
			if( isPersistent )
				cookie.setMaxAge(10000000);
			response.addCookie(cookie);
		}
	}

	public static void removeCookie(HttpServletRequest request,
									HttpServletResponse response,
									String name,
									String domain

	) {
		Cookie cookie = getCookie(request, name);
		if(cookie != null) {
			Cookie delCookie = new Cookie(name, "delete");
			delCookie.setPath("/");
			delCookie.setMaxAge(0);
			if (domain != null && domain.length() > 0)
				delCookie.setDomain(domain);
			response.addCookie(delCookie);
		}
	}


	public static String getRemoteAddr(HttpServletRequest request) {
		String addr = request.getHeader("X-Forwarded-For");
		if( addr==null )
			addr = request.getRemoteAddr();
		return addr;
	}

/*
	public static boolean authenticate(HttpServletRequest request,HttpServletResponse response,String authRealm,String authUsernameAndPassword)
		throws IOException
	{
		String auth = request.getHeader("Authorization");
		if( auth==null ) {
			response.setHeader("WWW-Authenticate","Basic realm=\""+authRealm+"\"");
			response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
			return false;
		}
		String[] a = auth.split(" +");
		if( a.length != 2 )
			throw new RuntimeException("auth = "+auth);
		if( !a[0].equals("Basic") )
			throw new RuntimeException("auth = "+auth);
		if( !new String(Base64.decode(a[1])).equals(authUsernameAndPassword) ) {
			response.setHeader("WWW-Authenticate","Basic realm=\""+authRealm+"\"");
			response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
			return false;
		}
		return true;
	}
*/
	public static String getAuthorization(HttpServletRequest request) {
		String auth = request.getHeader("Authorization");
		if( auth==null )
			return null;
		String[] a = auth.split(" +");
		if( a.length != 2 )
			throw new RuntimeException("auth = "+auth);
		if( !a[0].equals("Basic") )
			throw new RuntimeException("auth = "+auth);
		return new String(Base64.decode(a[1]));
	}

	public static void sendAuthenticate(HttpServletResponse response,String authRealm)
		throws IOException
	{
		response.setHeader("WWW-Authenticate","Basic realm=\""+authRealm+"\"");
		response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
	}
}
